package org.amos.server.modules.upms.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.amos.core.basic.constant.SystemConstant;
import org.amos.core.basic.exception.ServiceException;
import org.amos.core.basic.exception.TenantException;
import org.amos.core.basic.utils.AmosUtils;
import org.amos.core.basic.utils.JsonUtils;
import org.amos.core.frame.utils.IdUtils;
import org.amos.satoken.domain.bo.AuthInfo;
import org.amos.satoken.utils.UserUtils;
import org.amos.server.modules.upms.dto.TenantDTO;
import org.amos.server.modules.upms.dto.TenantRoleDTO;
import org.amos.server.modules.upms.dto.UserDTO;
import org.amos.server.modules.upms.entity.Tenant;
import org.amos.server.modules.upms.entity.TenantPackage;
import org.amos.server.modules.upms.mapper.TenantMapper;
import org.amos.server.modules.upms.service.IRoleService;
import org.amos.server.modules.upms.service.ITenantPackageService;
import org.amos.server.modules.upms.service.ITenantService;
import org.amos.server.modules.upms.service.IUserService;
import org.amos.tenant.core.TenantContextHolder;
import org.amos.tenant.properties.TenantProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class TenantServiceImpl extends ServiceImpl<TenantMapper, Tenant> implements ITenantService {
    private final ITenantPackageService tenantPackageService;
    private final IRoleService roleService;
    private final TenantProperties tenantProperties;
    @Autowired
    @Lazy
    private IUserService userService;

    @Override
    public Boolean remove(Set<Long> ids) {
        // 系统默认ID 数据不能删除
        boolean contains = CollUtil.contains(ids, SystemConstant.SYS_DEFAULT_ID);
        if (contains) {
            throw new ServiceException("系统内置数据不能变更！");
        }

        UpdateWrapper<Tenant> uw = new UpdateWrapper<>();
        uw.in(AmosUtils.toDbField(Tenant::getId), ids).set(AmosUtils.toDbField(Tenant::getIsDeleted), SystemConstant.SYS_DELETE_FLAG_ALREADY);
        super.update(uw);
        return Boolean.TRUE;
    }

    @Override
    public Long getTenantId() {
        if (!tenantProperties.getEnabled()) {
            return -1L;
        }
        return TenantContextHolder.getRequiredTenantId();
    }

    @Override
    public Tenant getTenant() {
        return super.getById(this.getTenantId());
    }

    @Override
    public Tenant getTenantByNo(String tenantNo) {
        QueryWrapper<Tenant> qw = new QueryWrapper<>();
        qw.eq(AmosUtils.toDbField(Tenant::getNo), tenantNo);
        qw.eq(AmosUtils.toDbField(Tenant::getIsDeleted), SystemConstant.SYS_DELETE_FLAG_DEFAULT);
        return super.getOne(qw);
    }

    @Override
    public void valid(Long tenantId) {
        Assert.state(Objects.nonNull(tenantId), "tenantId is null");
        QueryWrapper<Tenant> qw = new QueryWrapper<>();
        qw.eq(AmosUtils.toDbField(Tenant::getId), tenantId);
        qw.eq(AmosUtils.toDbField(Tenant::getIsDeleted), SystemConstant.SYS_DELETE_FLAG_DEFAULT);
        Tenant tenant = super.getOne(qw);

        if (Objects.isNull(tenant)) {
            throw new TenantException("租户已失效");
        }

        if (SystemConstant.SYS_INVALID_STATUS.equals(tenant.getStatus())) {
            throw new TenantException("该租户已禁用");
        }

        if (new Date().compareTo(tenant.getExpireTime()) >= 0) {
            throw new TenantException("租户已过期");
        }

        AuthInfo authInfo = UserUtils.getUser();
        // 登录用户校验tenantId合法性
        if (Objects.nonNull(authInfo) && !Objects.equals(tenantId, authInfo.getTenantId())) {
            throw new RuntimeException("this tenantId not equals users tenantId");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveOrUpdateTenant(TenantDTO dto) {
        Tenant tenant = AmosUtils.copy(dto, Tenant.class);
        // 查询并校验套餐
        TenantPackage tenantPackage = tenantPackageService.selectById(dto.getPackageId());
        boolean invalid = Objects.nonNull(tenantPackage) && SystemConstant.SYS_INVALID_STATUS.equals(tenantPackage.getStatus());
        if (Objects.isNull(tenantPackage) || invalid) {
            throw new ServiceException("租户套餐已失效,请重新选择!");
        }
        Set<Long> menuIds = JsonUtils.jsonToList(tenantPackage.getMenuIds(), Long.class).stream().collect(Collectors.toSet());
        // 新增操作校验租户编号
        Long tenantId = tenant.getId();
        Tenant tenantInfo = this.getTenantByNo(dto.getNo());
        if (Objects.nonNull(tenantInfo) && Objects.isNull(tenantId)) {
            throw new ServiceException("租户编号已存在,请勿重复添加!");
        }

        //创建租户
        DateTime dateTime = DateUtil.endOfDay(dto.getExpireTime()).offset(DateField.MILLISECOND,-999);
        tenant.setExpireTime(dateTime);
        if (Objects.isNull(tenantId)) {
            tenantId = IdUtils.nextId();
            tenant.setId(tenantId);
            //创建角色、用户信息
            TenantRoleDTO roleDTO = new TenantRoleDTO();
            roleDTO.setTenantId(tenantId);
            roleDTO.setRoleName(SystemConstant.STS_TENANT_ADMIN_ROLE_NAME);
            roleDTO.setRoleCode(SystemConstant.STS_TENANT_ADMIN_ROLE_ALIAS);
            roleDTO.setPermissions(menuIds);
            Long roleId = roleService.saveTenantRole(roleDTO);
            UserDTO userDTO = new UserDTO();
            userDTO.setTenantId(tenantId);
            userDTO.setUserName(dto.getUsername());
            userDTO.setPassword(dto.getPassword());
            userDTO.setNickName(SystemConstant.STS_TENANT_ADMIN_USER_NAME);
            userDTO.setRoleIds(new HashSet<>(Arrays.asList(roleId)));
            userDTO.setIsAdmin(SystemConstant.SYS_IS_ADMIN);
            userDTO.setStatus(SystemConstant.SYS_EFFECTIVE_STATUS);
            Long userId = userService.saveOrUpdateUser(userDTO);
            tenant.setContactUserId(userId);
        }else {
            TenantRoleDTO roleDTO = new TenantRoleDTO();
            roleDTO.setTenantId(tenantId);
            roleDTO.setRoleCode(SystemConstant.STS_TENANT_ADMIN_ROLE_ALIAS);
            roleDTO.setPermissions(menuIds);
            roleService.updateTenantRole(roleDTO);
        }
        super.saveOrUpdate(tenant);

        return Boolean.TRUE;
    }
}